Skip to content

fix(desktop): suppress Windows console flashes and reject WSL bash alias#2587

Merged
wpfleger96 merged 2 commits into
mainfrom
wpfleger96/windows-no-console-flash
Jul 23, 2026
Merged

fix(desktop): suppress Windows console flashes and reject WSL bash alias#2587
wpfleger96 merged 2 commits into
mainfrom
wpfleger96/windows-no-console-flash

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 23, 2026

Copy link
Copy Markdown
Member

What

Two defects cause visible console windows during normal Buzz operation on Windows.

1. Missing CREATE_NO_WINDOW on background spawn sites

Every std::process::Command or tokio::process::Command spawn of a console-subsystem binary from a GUI (non-console) parent on Windows allocates a fresh, briefly-visible console window per child unless CREATE_NO_WINDOW (0x0800_0000) is set. Every probe, install, auth check, git command, agent launch, and MCP server spawn had unguarded spawns.

2. WSL app-execution-alias footgun (issue #2328)

Git Bash resolution (both desktop-side and inside buzz-dev-mcp) could select %LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe, which is a WSL stub launcher. Running it spawns wsl.exe/wslhost.exe/conhost.exe trees instead of executing the intended shell command.

Fix

Per-crate configure_no_window helpers — one helper per crate, no cross-crate abstraction:

  • desktop/src-tauri/src/util.rs: pub(crate) fn configure_no_window(command: &mut std::process::Command) — called at every background spawn site in the desktop crate
  • crates/buzz-acp/src/acp.rs: fn configure_no_window(cmd: &mut tokio::process::Command) — applied to AcpClient::spawn
  • crates/buzz-dev-mcp/src/lib.rs: configure_no_window (std) + configure_no_window_async (tokio) — applied to rg.rs and shell.rs respectively
  • crates/buzz-agent/src/mcp.rs: fn configure_no_window(cmd: &mut Command) — applied to spawn_one() before TokioChildProcess::new, suppressing console flashes for Node-backed MCP server children

Desktop spawn sites covered (via crate::util::configure_no_window):

  • commands/agent_auth.rs: ACP auth command, Claude subscription login
  • commands/agent_discovery/managed_node.rs: managed-node version probe
  • commands/agent_model_process.rs: ACP model-listing spawn
  • commands/media_transcode.rs: ffmpeg invoke
  • commands/project_git_exec.rs: git exec
  • commands/relay_reconnect.rs: relay probe
  • managed_agents/backend.rs: provider invoke
  • managed_agents/discovery.rs: login-shell PATH probe, auth status probe, codex ACP version probe
  • managed_agents/readiness/cli_probe.rs: CLI readiness probe

Explicitly excluded (legitimately want a visible console or platform-specific):

  • commands/project_terminal.rs: launch_visible_terminal uses CREATE_NEW_CONSOLE on Windows
  • commands/agent_auth.rs terminal launch sites: same
  • commands/window_chrome.rs: macOS-only defaults read
  • managed_agents/process_lifecycle.rs: taskkill already sets CREATE_NO_WINDOW
  • shutdown.rs: macOS-only app relaunch (mesh-llm feature gate)
  • managed_agents/agent_env.rs: Command::new("env") calls are test-only
  • managed_agents/runtime.rs: agent spawn (PR 1 frozen, disjoint file set)

WSL bash alias rejection — both resolver paths protected:

Desktop (managed_agents/git_bash.rs):

  • is_windows_apps_alias(path: &Path) -> bool — pure path-structural predicate, cfg(any(windows, test)), testable cross-host
  • scan_path_for_bash filters resolved paths through the predicate before returning
  • 6 cross-host tests covering: typical path, case-insensitivity, real Git Bash (not rejected), System32 (not rejected), partial-segment (not rejected), Unix path (not rejected)

buzz-dev-mcp (crates/buzz-dev-mcp/src/shell.rs):

  • is_windows_apps_alias(path: &Path) -> bool — same pure predicate, cfg(any(windows, test))
  • scan_path_for_bash now does inline iteration filtering both %SystemRoot% and WindowsApps — alias-first/real-bash-second ordering selects the real one; alias-only returns None
  • 6 cross-host predicate tests + 2 Windows resolver tests (alias-first/real-second, alias-only)

Non-Windows impact

None. Every configure_no_window helper is a no-op off-Windows (#[cfg(not(windows))] let _ = cmd;). The is_windows_apps_alias functions are #[cfg(any(windows, test))]; scan_path_for_bash is Windows-only in production.

Fixes #2490, Fixes #2328, Fixes #2413
Supersedes #2493, #2416, #2488, #2541 (Will closes those; don't touch them)

Every background std::process::Command or tokio::process::Command spawn
from a GUI parent on Windows pops a visible console window unless
CREATE_NO_WINDOW (0x0800_0000) is set.  Installs, probes, auth checks,
git commands, model-list calls, relay reconnects, media transcodes, and
ACP agent launches all had unguarded spawns.

Also: Git Bash resolution could select %LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe,
a WSL stub that spawns wsl.exe / wslhost.exe / conhost.exe trees.  Reject it.

## CREATE_NO_WINDOW coverage

desktop/src-tauri (shared helper in util::configure_no_window):
  - commands/agent_auth.rs: acp auth command, Claude subscription login
  - commands/agent_discovery/managed_node.rs: managed-node version probe
  - commands/agent_model_process.rs: ACP model-listing spawn
  - commands/media_transcode.rs: ffmpeg invoke
  - commands/project_git_exec.rs: git exec
  - commands/relay_reconnect.rs: relay probe
  - managed_agents/backend.rs: provider invoke
  - managed_agents/discovery.rs: login-shell PATH probe, auth status probe,
    codex ACP version probe
  - managed_agents/readiness/cli_probe.rs: CLI readiness probe

Excluded (legitimately need a visible console or macOS/Windows-only):
  - commands/project_terminal.rs: launch_visible_terminal (explicit
    CREATE_NEW_CONSOLE on Windows, terminal emulator on Linux)
  - commands/agent_auth.rs: launch_visible_terminal call sites
  - commands/window_chrome.rs: macOS 'defaults' read (macOS-only)
  - managed_agents/process_lifecycle.rs: taskkill already sets CREATE_NO_WINDOW
  - shutdown.rs: app relaunch (macOS-only, mesh-llm feature gate)
  - managed_agents/agent_env.rs: Command::new("env") calls are test-only
  - managed_agents/runtime.rs: agent spawn (PR 1 frozen file)

crates/buzz-acp (configure_no_window helper in acp.rs):
  - acp.rs: AcpClient::spawn — agent subprocess launch

crates/buzz-dev-mcp (configure_no_window / configure_no_window_async in lib.rs):
  - rg.rs: system rg invocation
  - shell.rs: bash shell tool commands (tokio::process::Command)

## WindowsApps bash alias rejection

managed_agents/git_bash.rs:
  - is_windows_apps_alias(path: &Path) -> bool — pure path-structural
    predicate, cfg(any(windows, test)), testable on macOS CI
  - scan_path_for_bash filters resolved paths through the predicate
  - 6 cross-host tests in windows_apps_tests module

Fixes #2490, Fixes #2328, Fixes #2413
Supersedes #2493, #2416, #2488, #2541

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 requested a review from a team as a code owner July 23, 2026 18:46
Three MUST FIX items resolved:

1. helpers-after-test-module lint: move configure_no_window in
   buzz-acp/src/acp.rs and desktop/src-tauri/src/util.rs to before
   their respective mod tests blocks; remove unused
   std::os::windows::process::CommandExt import from tokio variants
   in acp.rs and buzz-dev-mcp/src/lib.rs (tokio exposes creation_flags
   natively, no import needed).

2. MCP server children CREATE_NO_WINDOW: add configure_no_window helper
   and call it in spawn_one() in crates/buzz-agent/src/mcp.rs before
   TokioChildProcess::new, suppressing console flashes for Node-backed
   MCP servers on Windows. cfg-gated no-op off Windows. Cross-host
   regression test added.

3. buzz-dev-mcp WSL alias rejection: add is_windows_apps_alias pure
   predicate (cfg(any(windows,test)), component-wise case-insensitive)
   in shell.rs and wire it into scan_path_for_bash so the WindowsApps
   stub launcher is skipped during PATH iteration — alias-first/real-
   bash-second scenario selects the real one. Six cross-host predicate
   tests + two Windows resolver tests (alias-first/real-second and
   alias-only) added.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 merged commit 5afa161 into main Jul 23, 2026
32 checks passed
@wpfleger96
wpfleger96 deleted the wpfleger96/windows-no-console-flash branch July 23, 2026 22:27
brow pushed a commit that referenced this pull request Jul 24, 2026
…r-only

* origin/main: (28 commits)
  Clarify agent harness defaults in create flow (#2601)
  chore(mobile): relax release check (#2636)
  fix: expose community icon control on open relays (#2640)
  chore(release): release Buzz Desktop version 0.4.24 (#2627)
  feat: remember per-community navigation location (#2629)
  fix(desktop): suppress Windows console flashes and reject WSL bash alias (#2587)
  fix(desktop): fix Windows PATH clobber and .cmd shim EINVAL (#2563)
  Update SECURITY.md
  chore(mobile): lighter-weight release process (#2144)
  Gate default relay auto-connect behind release flag (#2589)
  fix(desktop): fast-track relay restart reconnects (#2579)
  fix(sharing): preserve agent/team snapshot tEXt chunks through media sanitization (#2438)
  fix(acp): restrict DM turns to owner and verified siblings (#2591)
  test(desktop): live relay kill/restart reconnect gate (#2583)
  fix(relay): send 1012 restart close to all clients on graceful drain (#2575)
  fix(desktop): retry failed initial relay dials (#2564)
  Refine channel lifecycle settings (#2427)
  Fix avatar upload lifecycle edge cases (#2277)
  fix(observer): eager archive hydration on panel open + 200-frame pages (#2574)
  fix(cli): install rustls crypto provider to unbreak WSS publishes in release builds (#2590)
  ...

Co-authored-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz>
Signed-off-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz>

# Conflicts:
#	desktop/src-tauri/build.rs
#	desktop/src/features/agents/ui/AgentDefinitionDialog.tsx
#	desktop/src/testing/e2eBridge.ts
#	desktop/tests/helpers/bridge.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant